package com.arlin.spring.context;

import com.arlin.spring.factory.DefaultBeanFactory;

import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @ClassName: ApplicationContext
 * @Description: 程序上下文
 * @Author: arlin
 * @Date: 2021/12/11
 */
public class ApplicationContext implements SpringContext {

    private static SpringContext springContext;

    private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>();

    private final Map<String, Object> IOCContext = new ConcurrentHashMap<>();

    /**
     * single_bean_names_type_map
     * key: class的全限定类名
     * value: if(class是接口) 存放IOC容器中bean实现了该接口的全部全限定类名
     *        else 存放IOC容器中bean是class父类的全部全限定类名
     */
    private final Map<String, String[]> SINGLE_BEAN_NAMES_TYPE_MAP = new ConcurrentHashMap<>(128);

    public ApplicationContext() {
        springContext = this;
    }

    /**
     * 获取IOC容器指定类型的所有beanName到bean的映射
     *
     * @param type 指定类型
     * @return beanName到bean的映射
     */
    public <T> Map<String, T> getBeansOfType(Class<T> type) {
        Map<String, T> result = new HashMap<>();
        String[] beanNames = getBeanNamesForType(type);
        for (String beanName : beanNames) {
            Object beanInstance = IOCContext.get(beanName);
            if (!type.isInstance(beanInstance)) {
                throw new RuntimeException("not fount bean implement，the bean :" + type.getName());
            }
            result.put(beanName, type.cast(beanInstance));
        }
        return result;

    }

    /**
     * 获取IOC容器指定类型的所有beanName
     *
     * @param type 指定类型
     * @return beanNames
     */
    private String[] getBeanNamesForType(Class<?> type) {
        String beanName = type.getName();
        String[] beanNames = SINGLE_BEAN_NAMES_TYPE_MAP.get(beanName);
        if (beanNames == null) {
            Set<Map.Entry<String, Object>> entries = IOCContext.entrySet();
            beanNames = DefaultBeanFactory.getBeanNames(type, entries);
            SINGLE_BEAN_NAMES_TYPE_MAP.put(beanName, beanNames);
        }
        return beanNames;
    }

    @Override
    public Object getBean(String beanName) {
        return IOCContext.get(beanName);
    }

    @Override
    public Object getBean(Class<?> clazz) {
        return IOCContext.get(clazz.getSimpleName());
    }

    @Override
    public void setBean(String beanName, Object bean) {
        IOCContext.put(beanName, bean);
    }

    @Override
    public void setBean(Class<?> clazz, Object bean) {
        IOCContext.put(clazz.getSimpleName(), bean);
    }

    @Override
    public void dispose() {
        IOCContext.clear();
    }

    @Override
    public Map<String, Object> getIOCContext() {
        return IOCContext;
    }

    @Override
    public void setDefinition(String beanName, BeanDefinition beanDefinition) {
        beanDefinitionMap.put(beanName, beanDefinition);
    }

    @Override
    public BeanDefinition getDefinition(String beanName) {
        return beanDefinitionMap.get(beanName);
    }

    public static SpringContext getSpringContext() {
        return springContext;
    }

    public Map<String, String[]> getSINGLE_BEAN_NAMES_TYPE_MAP() {
        return SINGLE_BEAN_NAMES_TYPE_MAP;
    }
}
